1 module features.android_ndk; 2 public import feature; 3 import commons; 4 enum TargetAndroidNDK = "28.0.12433566"; 5 6 Feature AndroidNDKFeature; 7 8 9 10 enum FindAndroidNdkResult 11 { 12 NotFound, 13 Found, 14 MustInstallSdk, 15 MustInstallNdk 16 } 17 18 private FindAndroidNdkResult tryFindAndroidNDK(ref Terminal t, ref RealTimeConsoleInput input) 19 { 20 if("ANDROID_NDK_HOME" in environment) 21 { 22 configs["androidNdkPath"] = environment["ANDROID_NDK_HOME"]; 23 string sdk = getFirstExistingVar("ANDROID_SDK", "ANDROID_SDK_HOME"); 24 if(!sdk.length) 25 sdk = buildNormalizedPath(configs["androidNdkPath"].str, "..", ".."); 26 configs["androidSdkPath"] = sdk; 27 return FindAndroidNdkResult.Found; 28 } 29 bool isValidNDK(string chosenNDK) 30 { 31 import std.conv:to; 32 int ndkVer = chosenNDK[0..2].to!int; 33 return ndkVer <= 21; 34 } 35 version(Windows) 36 { 37 string locAppData = environment["LOCALAPPDATA"]; 38 if(locAppData == null) 39 { 40 t.writelnError("Could not find %LOCALAPPDATA% in your Windows."); 41 t.flush; 42 return FindAndroidNdkResult.NotFound; 43 } 44 string sdkPath = buildNormalizedPath(locAppData, "Android", "Sdk"); 45 string tempNdkPath = sdkPath; 46 47 if(!std.file.exists(sdkPath)) 48 { 49 t.writelnError("Could not find ", sdkPath, ". You need to install Android SDK."); 50 t.flush; 51 return FindAndroidNdkResult.MustInstallSdk; 52 } 53 tempNdkPath = buildNormalizedPath(sdkPath, "ndk"); 54 if(!std.file.exists(tempNdkPath)) 55 { 56 t.writelnError("Could not find ", tempNdkPath, ". You need to have at least one NDK installed."); 57 t.flush; 58 return FindAndroidNdkResult.MustInstallNdk; 59 } 60 do 61 { 62 string ndkPath = selectInFolder( 63 "Select the NDK which you want to use. Remember that only NDK <= 21 is supported.", 64 tempNdkPath, t, input 65 ); 66 if(isValidNDK(ndkPath)) 67 { 68 tempNdkPath = ndkPath; 69 break; 70 } 71 t.writelnError("Please select a valid NDK (<= 21)"); 72 } while(true); 73 t.writelnSuccess("Chosen "~ tempNdkPath~ " as your NDK."); 74 environment["androidNdkPath"] = sdkPath; 75 environment["androidSdkPath"] = tempNdkPath; 76 return FindAndroidNdkResult.Found; 77 } 78 else version(linux) 79 { 80 return FindAndroidNdkResult.NotFound; 81 } 82 else version(OSX) 83 { 84 return FindAndroidNdkResult.NotFound; 85 } 86 } 87 88 89 private bool installAndroidNDK(ref Terminal t, ref RealTimeConsoleInput input, TargetVersion ver, Download[] content) 90 { 91 string finalOutput = buildNormalizedPath(configs["androidSdkPath"].str, "cmdline-tools", "latest"); 92 string sdkManagerPath = buildNormalizedPath(finalOutput, "bin"); 93 string execSdkManager = "sdkmanager "; 94 version(Posix) execSdkManager = "./sdkmanager"; 95 string packagesToInstall = `"ndk;`~ver.toString~`" `; 96 97 t.writelnHighlighted("Installing packages: ", packagesToInstall, " \n\t", "You may need to accept some permissions, this process may take a little bit of time."); 98 t.flush; 99 if(t.wait(spawnShell("cd "~sdkManagerPath~" && "~execSdkManager ~" " ~packagesToInstall)) != 0) 100 { 101 t.writeln("Failed on installing NDK."); 102 t.flush; 103 return false; 104 } 105 106 107 configs["androidNdkPath"] = buildNormalizedPath(configs["androidSdkPath"].str, "ndk", ver.toString); 108 version(linux) 109 { 110 string binPath = buildNormalizedPath(configs["androidNdkPath"].str, "toolchains", "llvm", "prebuilt", "linux-x86_64", "bin"); 111 makeFileExecutable(buildNormalizedPath(binPath, "aarch64-linux-android35-clang")); 112 makeFileExecutable(buildNormalizedPath(binPath, "ld.lld")); 113 } 114 updateConfigFile(); 115 return true; 116 } 117 118 void initialize() 119 { 120 AndroidNDKFeature = Feature( 121 "Android NDK", 122 "Android Native Development Kit. Required for developing in other languages than Java/Kotlin for Android", 123 ExistenceChecker(["androidNdkPath"], null), 124 Installation(null, toDelegate(&installAndroidNDK)), 125 null, 126 VersionRange.parse(TargetAndroidNDK) 127 ); 128 } 129 void start() 130 { 131 import features.android_sdk; 132 AndroidNDKFeature.dependencies = [&AndroidSDKFeature]; 133 }